# -*- encoding: utf-8 -*-
"""
File Name: dedicated_line_monitor_pipeline
Description: Implements the DedicatedLineMonitorPipeline class for monitoring dedicated lines.
Author : 'lirui'
date: 10/24/2024
Contact: marjey_lee@163.com
Change Activity:
10/24/2024:
"""
import ll
from projects.monitoring.common.do.alarm import Alarm
from projects.monitoring.common.utils.redis_keys_util import RedisKeysUtil
from projects.monitoring.common.workflow.monitoring_pipline_interface import MonitorPipLineInterface
from projects.monitoring.dedicated_line_monitor.utils.load_cmdb_info import load_dedicated_info_from_cmdb
from projects.monitoring.dedicated_line_monitor.utils.router_connection import build_router_connection_mapping, build_router_connection


class DedicatedLineMonitorPipeline(MonitorPipLineInterface):
    """
    A class to monitor dedicated lines by checking the connectivity of routers.

    Attributes:
        _MONITORING_TYPE (str): The type of monitoring.
        router_connection_mapping (dict): A mapping of router names to their connections.
        results (dict): A dictionary to store the results of the ping checks.
    """
    _MONITORING_TYPE = RedisKeysUtil.get_dedicated_line_monitor_publisher_keys()

    def __init__(self, monitor_interval=1):
        """
        Initializes the DedicatedLineMonitorPipeline with the monitoring type and router connection mapping.
        """
        super().__init__(self._MONITORING_TYPE, monitor_interval)
        self.router_connection_mapping = build_router_connection_mapping()
        self.results = {}

    def _load_original_alarms(self):
        """
        Loads the original alarms by checking the connectivity of routers.

        Returns:
            None
        """
        check_res = []
        cmdb_data = load_dedicated_info_from_cmdb()
        for data in cmdb_data:
            ip = data['opposite_ip_address']
            router = data['router']
            name = data['institution']
            if 'FD_YNKM_EXT_Other' in router:
                continue
            ll.get_logger().debug(f'Start to check {name}')
            ll.sleep(0.1)
            if self._check_ping(ip, router):  # success
                self.results[f'{ip}-{router}'] = 1
                continue
            else:
                self.results[f'{ip}-{router}'] = 0
                d = dict(
                    timestamp=ll.TimeUtil.get_current_timestamp(),
                    router=router,
                    ip=ip,
                    name=name,
                    importance=data['importance']
                )
                check_res.append(d)
        return check_res

    def _format_alarms(self, original_alarms) -> list[Alarm]:
        """
        Abstract method to format alarms. Must be implemented by subclasses.

        Args:
            original_alarms (list): A list of original alarms.

        Returns:
            list: A list of formatted alarms.
        """
        ret = []
        for p in original_alarms:
            if '重要' == p['importance']:
                severity = 'fatal'
            else:
                severity = 'warning'
            alarm = Alarm(
                timestamp=p['timestamp'],
                ip=p['ip'],
                hostname=p['name'],
                severity=severity,
                alarm_type='alarm',
                alarm_info=f"【外联专线异常】{p['name']}-{p['ip']}-{p['router']}",
                recovery=False,
                manager_ip=p['router'],
                manager_port=0,
                alarm_source=p['router']
            )
            ret.append(alarm)
        return ret

    def _check_ping(self, ip, router_name):
        """
        Checks the ping connectivity to a given IP through a specified router.

        Args:
            ip (str): The IP address to ping.
            router_name (str): The name of the router.

        Returns:
            bool: True if the ping is successful, False otherwise.
        """
        ll.get_logger().debug(f'Start to check ping {ip} through {router_name}.')
        if router_name not in self.router_connection_mapping:
            self.router_connection_mapping[router_name] = build_router_connection(router_name)
        if router_name not in self.router_connection_mapping:
            ll.get_logger().warning(f'{router_name} connection failed.')
            return False
        if not self._self_check_connection(self.router_connection_mapping[router_name]):
            self.router_connection_mapping[router_name] = build_router_connection(router_name)
            if not self._self_check_connection(self.router_connection_mapping[router_name]):
                ll.get_logger().error(f'check connection {router_name} error.')
                return False
        return self._check_ip(ip, router_name)

    # def _check_ip(self, ip, router_name):
    #     """
    #     Sends a ping command to check the connectivity to a given IP through a specified router.
    #
    #     Args:
    #         ip (str): The IP address to ping.
    #         router_name (str): The name of the router.
    #
    #     Returns:
    #         bool: True if the ping is successful, False otherwise.
    #     """
    #     cmd = f'ping -c 2 {ip}'
    #     for i in range(3):
    #         try:
    #             res = self.router_connection_mapping[router_name].send_command(
    #                 cmd,
    #                 read_timeout=30
    #             )
    #             if ' 0.00% packet loss' in res or ' 0.0% packet loss':
    #                 return True
    #         except Exception as e:
    #             ll.get_logger().warning(e)
    #     return False

    def _check_ip(self, ip, router_name):
        """
        Args:
            ip:
            router_name:

        Returns:
        """
        # 下发指令信息
        try:
            ll.get_logger().debug(f'Start to check ping {ip} through {router_name}.')
            if not self._self_check_connection(self.router_connection_mapping[router_name]):
                self.router_connection_mapping[router_name] = build_router_connection(router_name)
                if not self._self_check_connection(self.router_connection_mapping[router_name]):
                    ll.get_logger().error(f'check connection {router_name} error.')
                    return False
            cmd = f'ping -c 2 {ip}'
            for i in range(3):
                res = self.router_connection_mapping[router_name].send_command(
                    cmd,
                    read_timeout=20
                )
                if ' 0.00% packet loss' in res or ' 0.0% packet loss' in res:
                    return True
                elif " 100" in res:
                    print(router_name + "  " + ip + "   100.00% packet loss")
            return False
        except Exception as e:
            ll.get_logger().error(f'Check ping error {e}')
            return False

    @staticmethod
    def _self_check_connection(connection):
        """
        Checks the self-connectivity of a given connection.

        Args:
            connection: The connection to check.

        Returns:
            bool: True if the self-connectivity check is successful, False otherwise.
        """
        if connection is None:
            return False
        for _ in range(3):
            try:
                output = connection.send_command(
                    'ping  -c 1 127.0.0.1',
                    read_timeout=30,
                )
                if 'received' in output:
                    return True
            except Exception as e:
                ll.get_logger().warning(e)
        return False
